import datetime
import json
import logging
import os
import random
import re
import smtplib
import time
import uuid

from email.header import Header
from email.mime.text import MIMEText
from urllib.parse import parse_qs

from alipay import AliPay
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
from alipay.aop.api.util.SignatureUtils import verify_with_rsa
from django.contrib import messages
from django.contrib.auth import login, logout, user_logged_in
from django.db.models import Max
from django.dispatch import receiver
from django.http import HttpResponse, JsonResponse, HttpResponseServerError
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views.decorators.clickjacking import xframe_options_exempt
import requests
from django.views.decorators.csrf import csrf_exempt

import base_setting
from djangoApp import settings
from djangoApp.utils.enums import StatusCodeEnum
from djangoApp.utils.result import R
from .forms.signupform import signupform
from .models import *

# Create your views here.
from .util.book import bookUtil
from .util.menuUtil import *
from lucky.util.book.progressTracker import *


# <↓-----------------------------------------↓页面地址↓-----------------------------------------↓>
from .util.pay_alipay import create_alipay


@xframe_options_exempt
def toLoginView(request):
    return render(request, 'login.html')


@xframe_options_exempt
def wxLoginView(request):
    resp = {'code': 200, 'msg': '操作成功'}
    code = request.GET.get('code', '')

    if not code or len(code) < 1:
        resp['code'] = -1
        resp['msg'] = '需要code'
        return JsonResponse(resp)
    url = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code" \
        .format(base_setting.MINA_APP['appid'], base_setting.MINA_APP['appkey'], code)
    try:
        r = requests.get(url).json()
        # res = json.loads(r.text)
        openid = r['openid']
    except:
        return JsonResponse({'code': 400, 'msg': '获取openid失败'})
    return JsonResponse({'code': 200, 'msg': '操作成功', 'openid': openid})


@csrf_exempt
@xframe_options_exempt
def loginView(request):
    request.session.flush()
    arg = {}
    if request.user.is_authenticated:
        userinfo = queryUser(request.user.username)
        arg['userinfo'] = userinfo
        return render(request, 'index.html', arg)
    else:

        if request.method == "POST":
            user_name = request.POST.get('username', '')  # 后面的''表示获取不到user就赋值空
            pass_word = request.POST.get('password', '')

            if user_name and pass_word:
                exit_user = UserInfo.objects.filter(username=user_name, password=pass_word).count()
                if exit_user >= 1:
                    userinfo = queryUser(user_name)

                    firstmenu, secondmenu, thirdmenu = queryMenu(userinfo.is_superuser)
                    arg = {
                        'firstmenu': firstmenu,
                        'secondmenu': secondmenu,
                        'thirdmenu': thirdmenu,
                    }
                    arg['userinfo'] = userinfo
                    # Django 登录状态保持
                    login(request, userinfo)

                    request.session['is_login'] = True
                    request.session['user_id'] = userinfo.id
                    request.session['user_name'] = userinfo.username
                    return render(request, 'index.html', arg)
                else:
                    return render(request, 'login.html', {'script': "alert", 'wrong': '账号或密码错误'})


# 退出
@xframe_options_exempt
def logoutView(request):
    request.session.flush()
    logout(request)
    return render(request, 'login.html')


#   注册
def toSignView(request):
    return render(request, 'signup.html')


@xframe_options_exempt
def hello(request):
    if request.user.is_authenticated:
        return render(request, 'html/home.html')
    else:
        return render(request, 'login.html')


# AIChart
@xframe_options_exempt
def aiChart(request):
    if request.user.is_authenticated:
        day = datetime.datetime.now().date()
        log = models.ChartLog.objects.order_by("created_at").filter(username_id=request.user.id,
                                                                    created_at__contains=day)
        return render(request, 'html/aichart.html', {'chatlog': log})
    else:
        return render(request, 'login.html')


@xframe_options_exempt
def device(request):
    if request.user.is_authenticated:
        return render(request, 'html/device.html')
    else:
        return render(request, 'login.html')


# <↓-----------------------------------------↓实现方法↓-----------------------------------------↓>
# 接收信息返回结果
def aiLinkView(request):
    current_user = request.user

    context = request.GET.get('context', '')
    context = context.replace('\n', '').replace('\r', '')

    aiURL = Config.objects.filter(para='AIURL').values('value')[0]
    aiToken = Config.objects.filter(para='AITOKEN').values('value')[0]
    token = aiToken['value']
    url = aiURL['value']
    # 取历史3条文本
    his = reversed(ChartLog.objects.filter(username_id=request.user.id).order_by('-id')[:3])
    mes = []
    vel = {}
    for i in his:
        vel["role"] = "user"
        vel["content"] = i.question
        hisdic = vel.copy()
        mes.append(hisdic)

        vel["role"] = "assistant"
        vel["content"] = i.answer
        hisdic = vel.copy()
        mes.append(hisdic)

    vel["role"] = "user"
    vel["content"] = context
    mes.append(vel)

    data = {
        'token': token,
        'message': mes,
        # 'message': [{"role": "user", "content": context}],
        # 'content': context,
    }
    headers = {'Content-Type': 'application/json', }
    try:
        # 发送 POST 请求并设置请求头
        # response = requests.post(url, json=data, headers=headers, stream=True)不使用流方式

        response = requests.post(url, json=data, headers=headers, timeout=None)
        if response.encoding is None:
            response.encoding = 'utf-8'
        response.raise_for_status()
        text = json.loads(response.text)
        res = text['data']['content']
        # res = htmlFormat(res)  html格式化

        # 处理响应数据，并逐行输出
        # for line in response.iter_lines(decode_unicode=True):
        #     if line.strip():
        #         str = json.loads(line)
        #         print(str['text'])
        print(res)
    except requests.exceptions.RequestException as e:
        print(e)
        res = '抱歉,我生病了,不能回答你的问题'
    try:
        logdata = ChartLog(question=context, answer=res, username_id=current_user.id)
        logdata.save()
    except BaseException as e:
        pass
    return JsonResponse({'status': '200', 'content': res})


# 注册
def signUp(request):
    ztype = 'S'
    apptype = request.GET.get('apptype', '')  # 注册用户的app类型
    username = request.GET.get('username', '')
    realname = request.GET.get('realname', '')
    password = request.GET.get('password', '')
    confirm_pwd = request.GET.get('confirm_pwd', '')
    email = request.GET.get('email', '')
    phone = request.GET.get('phone', '')
    # mess = request.GET.get('message', '')
    if apptype == '':
        apptype = 1  # 主网站通用用户类型
    # 用户名 6-20个字符
    if not re.match(r'^[a-zA-Z0-9_]{6,20}', username):
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.USER_ERR)

    # 密码 6-20个字符
    if not re.match(r'^[a-zA-Z0-9]{6,20}', password) and ztype == 'S':
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.PWD_ERR)

    # 两次密码一致性
    if password != confirm_pwd and ztype == 'S':
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.CPWD_ERR)

    # 手机号合法性
    if not re.match(r'^1[3-9]\d{9}$', phone) and phone != '' and ztype == 'S':
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.MOBILE_ERR)
    # 根据user_name 判断用户是否存在数据库中=>增加apptype一起验证
    res_username = UserInfo.objects.filter(username=username, apptype_id=int(apptype)).count()
    if phone != '':
        res_phone = UserInfo.objects.filter(phone=phone).count()
    else:
        res_phone = 0
        phone = ' '

    if res_username != 0 and ztype == 'S':
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.USER_CF)
    if res_phone != 0 and phone != 0 and ztype == 'S':
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.MOBILE_CF)

    # 注册时赠送7天试用
    datetime_end = (datetime.datetime.now() + timedelta(days=7)).strftime('%Y-%m-%d %H:%M:%S')

    if ztype != 'E':
        try:
            user = UserInfo.objects.create(username=username, real_name=realname, password=password, email=email,
                                           phone=phone, apptype_id=apptype, end_data=datetime_end,
                                           last_login=timezone.now().strftime('%Y-%m-%d %H:%M:%S'),
                                           is_staff=1, is_active=1)
        except BaseException as e:
            print(str(e))
            ztype = 'E'
            my_alert(request, ztype, StatusCodeEnum.REGISTER_FAILED_ERR)
        else:
            ztype = 'S'
            my_alert(request, ztype, StatusCodeEnum.OK_SIGN)

    # return render(request, 'signup.html', messages)
    # 返回JSON响应
    if is_ajax(request=request):
        tempName = 'myAlert.html'
    else:
        tempName = 'signup.html'
    all_messages = messages.get_messages(request)
    return render(request, tempName, {'messages': all_messages})


# 发送邮件验证
def sendMes(request):
    ztype = 'S'
    # 获取邮箱
    data = '%06d' % random.randint(100000, 999999)
    email = request.GET.get('email', '')
    if email == '':
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.EM_NULL_ERR)
    else:
        try:
            obj = Redis.objects.get(email=email)
            is_expired = obj.is_expired()
        except:  # 没读到,就新增
            res = Redis(email=email, msmcode=data)
            res.save()
        else:
            if is_expired:  # 已过期,重新生成
                res = Redis(email=email, msmcode=data)
                res.save()
            else:
                ztype = 'E'
                my_alert(request, ztype, StatusCodeEnum.EM_CODE_ERR)

    if ztype != 'E':
        username = request.GET.get('username', '')
        username = '亲爱的用户:' + username
        # 创建 SMTP 对象
        smtp = smtplib.SMTP()
        # 连接（connect）指定服务器
        smtp.connect("smtp.163.com", port=25)
        # 登录，需要：登录邮箱和授权码
        smtp.login(user="ljxcxyx001@163.com", password="QVGBBIVYRAHOYYQP")
        # 发送邮件
        html = "<h2>这里是轻松工坊!</h2><p>您的验证码为:</p><p><h3>" + data + "</h3></p>"
        message = MIMEText(html, 'html')
        message['From'] = Header('轻松工坊', "utf-8")  # 发件人的昵称
        message['To'] = Header(username, "utf-8").encode()  # 收件人的昵称
        message['Subject'] = Header('轻松工坊-注册/验证', "utf-8")  # 定义主题内容
        try:
            smtp.sendmail(from_addr="ljxcxyx001@163.com", to_addrs=email, msg=message.as_string())
            ztype = 'S'
            my_alert(request, ztype, StatusCodeEnum.OK_SEND)
            smtp.quit()
        except:
            ztype = 'E'
            my_alert(request, ztype, StatusCodeEnum.EM_SEND_ERR)
    # 返回JSON响应
    if is_ajax(request=request):
        tempName = 'myAlert.html'
    else:
        tempName = 'signup.html'
    all_messages = messages.get_messages(request)
    return render(request, tempName, {'messages': all_messages})
    # return HttpResponse(all_messages)使用自制弹窗时的返回值


def toChangePwdView(request):
    return render(request, 'changePwd.html')


# 修改密码
def changePd(request):
    ztype = 'S'
    username = request.GET.get('username', '')
    password = request.GET.get('password', '')
    confirm_pwd = request.GET.get('confirm_pwd', '')
    email = request.GET.get('email', '')
    mess = request.GET.get('message', '')
    res_username = UserInfo.objects.filter(username=username).count()

    if res_username == 0:
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.USER_ERR)

    # 密码 6-20个字符
    if not re.match(r'^[a-zA-Z0-9]{6,20}', password) and ztype == 'S':
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.PWD_ERR)

    # 两次密码一致性
    if password != confirm_pwd and ztype == 'S':
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.CPWD_ERR)

    try:
        obj = Redis.objects.filter(email=email, msmcode=mess).values('email').annotate(Max('id'))
        for s in obj:
            obj = Redis.objects.get(id=s['id__max'])
            is_expired = obj.is_expired()
    except:  # 没读到
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.EM_SENDNULL_ERR)
    else:
        if obj:
            if is_expired:  # 已过期
                ztype = 'E'
                my_alert(request, ztype, StatusCodeEnum.EM_OUT_ERR)
        else:
            ztype = 'E'
            my_alert(request, ztype, StatusCodeEnum.EM_SENDNULL_ERR)
    if ztype != 'E':
        try:
            user = UserInfo.objects.filter(username=username).update(password=password)
        except:
            ztype = 'E'
            my_alert(request, ztype, StatusCodeEnum.REGISTER_FAILED_ERR)
        else:
            ztype = 'S'
            my_alert(request, ztype, StatusCodeEnum.OK_CHANGE)

    if is_ajax(request=request):
        tempName = 'myAlert.html'
    else:
        tempName = 'changePwd.html'
    all_messages = messages.get_messages(request)
    return render(request, tempName, {'messages': all_messages})


# 检查用户名是否已存在
def checkUsername(request):
    ztype = 'S'
    username = request.GET.get('username', '')
    res_username = UserInfo.objects.filter(username=username).count()

    if res_username == 0:
        ztype = 'E'
        my_alert(request, ztype, StatusCodeEnum.USER_ERR)
    else:
        ztype = 'S'
        my_alert(request, ztype, StatusCodeEnum.OK_USER)

    tempName = 'myAlert.html'
    all_messages = messages.get_messages(request)
    return render(request, tempName, {'messages': all_messages})


# ------------------------------------------------------------------------------书相关↓
@xframe_options_exempt
def ebook(request):
    if request.user.is_authenticated:
        return render(request, 'html/ebook.html')
    else:
        return render(request, 'login.html')


def getAllBook(request):
    """
    获取EbookSrc中所有网址的书单书籍
    :param request:
    :return:
    """
    progress['no'] = progress['completed'] = progress['total'] = 0
    # list_url = EbookSrc.objects.all()  #取所有url
    list_url = EbookSrc.objects.all().order_by('srcid')[0:1]  # 前一条
    res = len(list_url)
    for i in list_url:
        progress['no'] += 1
        bookUtil.get_booklist(i.srcid, i.srcurl, i.listurl)
    dic = {}
    return render(request, 'html/ebook.html', dic)


def get_progress(request):
    """
    更新每个页面按钮的进度
    :param request:
    :return:
    """
    return JsonResponse(progress)


def updateAllBook(request):
    """
    更新所有已存在的书籍
    :param request:
    :return:
    """
    # list_url = EbookSrc.objects.all()  #取所有url
    list_url = EbookSrc.objects.all().order_by('srcid')[0:1]  # 前一条
    for i in list_url:
        bookUtil.update_ebook(i.srcid, i.srcurl, i.listurl)
    return render(request, '开始更新')


# ---------------------------------------------------------------------------书相关结束↑
# ---------------------------------------------------------------------------视频上传工具相关开始↓
@receiver(user_logged_in)
def on_user_logged_in(sender, request, user, **kwargs):
    user.last_login_ip = request.META.get('REMOTE_ADDR')  # 或者使用 'REMOTE_HOST'
    user.save(update_fields=['last_login_ip'])


def getVideoPcMac(request):
    pcmac = request.GET.get('pcmac', '')
    print(pcmac)
    try:
        res = VideoTool.objects.get(pcmac=pcmac)
        date = res.date_end
        nowTime = datetime.datetime.now()  # .strftime('%Y-%m-%d %H:%M:%S')
        if nowTime > date:
            status = '404'
            msg = '您的使用权限已到期,请联系管理员!'
        else:
            status = '200'
            msg = date
        print(msg)
    except BaseException as e:
        status = '404'
        msg = "您不具有使用权限!\r\n如需使用,请联系管理员!"
    return JsonResponse({'status': status, 'msg': msg})


def addVideoPcMac(request):
    '''
    申请试用,默认1天
    :param request:
    :return:
    '''
    pcmac = request.GET.get('pcmac', '')
    sumDays = request.GET.get('days', '')
    nowTime = datetime.datetime.now()  # .strftime('%Y-%m-%d %H:%M:%S')
    try:
        # 先查有没有试用过
        res = VideoTool.objects.get(pcmac=pcmac)
        date = res.date_end
        if nowTime > date:
            status = '404'
            msg = '您的试用权限已到期!\r\n如需继续使用,请联系管理员!'
        else:
            status = '200'
            msg = date
    except BaseException as e:
        try:
            days = int(sumDays)
            newDate = nowTime + timedelta(days=days)
            res = VideoTool.objects.create(pcmac=pcmac, date_end=newDate)
            status = '200'
            msg = newDate.strftime('%Y-%m-%d %H:%M:%S')
        except BaseException as ne:
            print(str(ne))
            status = '404'
            msg = "申请试用失败!\r\n请联系管理员!\r\n " + str(ne) + 'pcmac参数:' + pcmac + 'sumDays参数:' + sumDays
    return JsonResponse({'status': status, 'msg': msg})


def loginAPP(request):
    if request.method == "POST":
        user_name = request.POST.get('username', '')  # 后面的''表示获取不到user就赋值空
        pass_word = request.POST.get('password', '')
        if user_name and pass_word:
            exit_user = UserInfo.objects.filter(username=user_name, password=pass_word).count()
            if exit_user >= 1:
                userinfo = queryUser(user_name)

                # Django 登录状态保持
                login(request, userinfo)

                request.session['is_login'] = True
                request.session['user_id'] = userinfo.id
                request.session['user_name'] = userinfo.username

                return JsonResponse({'status': '200', 'msg': '登录成功'})
            else:
                return JsonResponse({'status': '404', 'msg': '账号或密码错误'})


# ---------------------------------------------------------------------------视频上传工具相关结束↑
# 检测是否是ajax请求
def is_ajax(request):
    return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'


# 状态栏弹窗消息
def my_alert(request, z_type, enum):
    # 清除所有的消息
    list(messages.get_messages(request))

    data = R.set_result(enum).data()
    msg = '消息号' + str(data['code']) + '@' + str(data['errmsg'])
    if z_type == 'S':
        # 成功消息
        messages.add_message(request, messages.SUCCESS, msg)
    elif z_type == 'W':
        # 警告消息
        messages.add_message(request, messages.WARNING, msg)
    elif z_type == 'E':
        # 错误消息
        messages.add_message(request, messages.ERROR, msg)


# 404
def page_not_found(request, exception):
    return render(request, '404.html')


# 403
def permission_denied(request, exception):
    return render(request, '403.html')


# 500
def page_error(request):
    return render(request, '500.html')


# <↓-----------------------------------------↓支付↓-----------------------------------------↓>
@csrf_exempt
def pays(request):
    if request.method == "GET":
        # 获取当前登录用户
        user = request.user

        # 获取用户的用户名和到期日期
        username = user.username
        end_data = user.end_data
        return render(request, 'pays.html',{'username': username,'end_data': end_data,})

def pay_post(request):
    if request.method == "POST":
        # 处理表单数据
        pay_data = request.POST  # 获取表单提交的数据

        paysum = pay_data.get('amount')#支付金额
        paydays = pay_data.get('days')#天数
        payname = pay_data.get('payname')#支付应用
        payway =  pay_data.get('payway')#支付端
        paytype =  pay_data.get('radio1')#支付方式

        # 创建订单
        order = OrderInfo.objects.create(
            pay_type=paytype,
            pay_sum=paysum,
            pay_way=payway,
            pay_name=payname,
            pay_days=paydays,
            user_name=request.user.username,
            status='PENDING',
            create_time=now(),
        )

        alipay = create_alipay()
        if payway == "PHONE":
            order_string = alipay.api_alipay_trade_wap_pay(
                out_trade_no=order.order_id,
                total_amount=str(order.pay_sum),
                subject=f"订单支付 - {order.order_id}",  # 支付标题,
                return_url="https://www.myluckyai.cn/index/pays.html",
                notify_url="https://www.myluckyai.cn/index/alipay_notify"
            )
            return redirect(settings.ALIPAY_GATEWAY_TEST + '?' + order_string)
        elif payway == "PC":
            # 构造支付请求参数
            order_string = alipay.api_alipay_trade_page_pay(
                out_trade_no=order.order_id,  # 订单号
                total_amount=str(order.pay_sum),  # 支付金额
                subject=f"订单支付 - {order.order_id}",  # 支付标题
                return_url="https://www.myluckyai.cn/index/alipay_notify",  # 同步回调 URL
                notify_url="https://www.myluckyai.cn/index/alipay_notify",  # 异步通知 URL
            )
            return redirect(settings.ALIPAY_GATEWAY_TEST + '?' + order_string)
        else:
            return HttpResponse("<a href=""/index/pays.html"">创建支付订单失败，点此返回</a>")
    else:
        return HttpResponse("请通过表单提交数据。")


@csrf_exempt  # 支持支付宝 POST 请求回调
def alipay_notify(request):
    """支付宝支付异步通知接口"""
    if request.method == "POST":
        # 初始化支付宝 SDK
        alipay = create_alipay()

        # 获取支付宝异步通知数据
        data = request.POST.dict()
        sign = data.pop("sign", None)

        # 验签（确保数据来自支付宝）
        is_valid = alipay.verify(data, sign)
        if is_valid:
            # 验签成功，处理业务逻辑
            trade_status = data.get("trade_status")
            order_id = data.get("out_trade_no")  # 商户订单号
            trade_id = data.get("trade_no")  # 支付宝交易号

            # 订单支付成功
            if trade_status == "TRADE_SUCCESS":
                try:
                    # 更新订单状态
                    order = OrderInfo.objects.get(order_id=order_id)
                    order.status = "SUCCESS"
                    order.trade_id = trade_id
                    order.pay_time = now()
                    pay_days = int(order.pay_days)
                    order.save()

                    # 更新用户的有效期
                    user = UserInfo.objects.get(username=order.user_name)
                    if user.end_data and user.end_data > now():
                        # 如果用户已有有效期，延长30天
                        user.end_data += timedelta(days=pay_days)
                    else:
                        # 如果用户没有有效期，设置为当前时间+30天
                        user.end_data = now() + timedelta(days=pay_days)
                    user.save()

                    return HttpResponse("success")  # 返回给支付宝通知已成功接收
                except OrderInfo.DoesNotExist:
                    return JsonResponse({'error': '订单不存在'}, status=400)
                except UserInfo.DoesNotExist:
                    return JsonResponse({'error': '用户不存在'}, status=400)
        else:
            return JsonResponse({'error': '验签失败'}, status=400)

    return JsonResponse({'error': '无效的请求方式'}, status=405)
